package com.web_bloks.types.client;

import java.util.HashMap;
 
/**
 * Simple Property Container class
 * 
 * @author Jean-Lou Dupont
 *
 */
public class PropertyContainer implements IPropertyContainer {

	/**
	 * The property map
	 */
	protected HashMap<String, Object> pmap = new HashMap<String, Object>();
	
	/**
	 * The property meta map
	 */
	protected HashMap<String, EPropertyMeta[]> pmetamap = new HashMap<String, EPropertyMeta[]>();
	
	/**
	 * Use the factory
	 */
	protected PropertyContainer() {
		super();
	}

	boolean lock = false;
	
	/**
	 * GETTER
	 */
	public Property getProperty(String key) throws IPropertyException {
		
		if (!pmap.containsKey(key))
			_raise(key, IPropertyException.Code.NOT_PRESENT);
		
		return constructProperty(key, pmap.get(key), pmetamap.get(key));
	}

	/**
	 * GETTER with default 
	 */
	public Property getProperty(String key, Property defaultValue) {
		
		if (!pmap.containsKey(key))
			return defaultValue;

		return constructProperty(key, pmap.get(key), pmetamap.get(key));
	}

	/**
	 * GETTER with default AND type check
	 */
	@SuppressWarnings("unchecked")
	public Property getProperty(String key, Property defaultValue, Class klass) throws IPropertyException {

		Property p = null;
		
		if (!pmap.containsKey(key))
			p = defaultValue;

		if (p.getClass() != klass )
			_raise(key, IPropertyException.Code.TYPE_MISMATCH);
		
		return constructProperty(key, pmap.get(key), pmetamap.get(key));
	}

	/**
	 * GETTER for meta information
	 */
	public EPropertyMeta[] getPropertyMeta(String key) throws IPropertyException {

		if (!pmetamap.containsKey(key))
			_raise(key, IPropertyException.Code.NOT_PRESENT);

		return pmetamap.get(key);
	}

	/**
	 * Verifies if a property is present
	 */
	public boolean hasKey(String key) {
		return pmap.containsKey(key);
	}

	/**
	 * Removes a property if allowed
	 * i.e. the property is not persistent
	 * 
	 */
	public Property removeKey(String key) throws IPropertyException {

		if (isLocked())
			throw new RuntimeException("instance is locked");
		
		if (!pmap.containsKey(key))
			_raise(key, IPropertyException.Code.NOT_PRESENT);

		if (hasMeta(key, EPropertyMeta.PERSISTENT))
			_raise(key, IPropertyException.Code.PERSISTENT);
		
		Property p = constructProperty(key, pmap.get(key), pmetamap.get(key));
		
		pmap.remove(key);
		
		return p;
	}
	/**
	 * Check if a property carries a certain meta 
	 */
	public boolean hasMeta(String key, EPropertyMeta meta) throws IPropertyException {
		
		if (!pmap.containsKey(key))
			_raise(key, IPropertyException.Code.NOT_PRESENT);
		
		return verySafeMetaLookup( key, meta );
	}//

	protected boolean verySafeMetaLookup(String key, EPropertyMeta meta) {
		
		EPropertyMeta[] metas = pmetamap.get(key);
		
		for(int i=0;i<metas.length;i++) {
			if ( metas[i] == meta )
				return true;
		}
		
		return false;		
	}
	
	protected boolean safeHasMeta(String key, EPropertyMeta meta ){
		
		if (!pmap.containsKey(key))
			return false;
		
		return verySafeMetaLookup( key, meta );
	}
	
	/**
	 * SETTER, if allowed, a property's value
	 */
	public void setProperty(Property value) throws IPropertyException {

		if (isLocked())
			throw new RuntimeException("instance is locked");

		if (null==value) 
			throw new RuntimeException( "value can't be null" );
		
		String key = value.getKey();
		
		// already an entry with a READ_ONLY restriction? 
		if (safeHasMeta(key, EPropertyMeta.READ_ONLY))
			_raise(key, IPropertyException.Code.READ_ONLY);
		
		pmap.put(key, value.getValue());
		pmetamap.put(key, value.getMeta() );
	}
	
	//======================================================================
	// HELPERS
	
	protected void _raise(String key, IPropertyException.Code code ) throws IPropertyException{
		throw new IPropertyException(key, code );
	}

	/**
	 * Generic Property Construction
	 *  
	 * @param key
	 * @param value
	 * @return property instance
	 */
	protected Property constructProperty(String key, Object value, EPropertyMeta[] metas) {
		Property P = Property.safeCreate(key, value, metas);
		return P;
	}
	
	//======================================================================
	// DEBUG
	
	public void setLock(boolean l) {
		this.lock = l;
	}
	
	public boolean isLocked() {
		return this.lock == true;
	}
	
}//